package com.lz.ovuola.service.impl;

import java.util.Date;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.lz.ovuola.entity.PhysiologicalBasisDo;
import com.lz.ovuola.entity.UserDo;
import com.lz.ovuola.repository.mysql.impl.PhysiologicalBasisRepository;
import com.lz.ovuola.repository.mysql.impl.UserRepository;
import com.lz.ovuola.service.SysSecurityService;
import com.lz.ovuola.service.assembler.LoginUserAssembler;
import com.lz.ovuola.util.annotation.TargetDataSource;
import com.lz.ovuola.util.auth.TokenGenerateUtils;
import com.lz.ovuola.util.codec.EncryptionUtils;
import com.lz.ovuola.util.concurrent.ThreadPoolExecutor;
import com.lz.ovuola.util.constant.DataSourceConsts;
import com.lz.ovuola.util.constant.RedisCacheConsts;
import com.lz.ovuola.util.exception.BusinessException;
import com.lz.ovuola.util.exception.msg.CommonMsgEnum;
import com.lz.ovuola.util.exception.msg.SysSecurityMsgEnum;
import com.lz.ovuola.util.exception.msg.UserMsgEnum;
import com.lz.ovuola.util.redis.RedisTemplateRepository;
import com.lz.ovuola.util.regex.RegexUtils;
import com.lz.ovuola.util.response.ResponseDataPackageUtils;
import com.lz.ovuola.util.sms.SmsCodeUtils;
import com.lz.ovuola.util.threadlocal.UserContext;

/**
 * 
 * @author fz
 *
 */
@Service("sysSecurityService")
public class SysSecurityServiceImpl implements SysSecurityService {

	@Autowired
	private RedisTemplateRepository redisTemplateRepository;

	@Resource
	private UserRepository userRepository;

	@Resource
	private PhysiologicalBasisRepository physiologicalBasisRepository;

	@Autowired
	private StringRedisTemplate stringRedisTemplate;
	/**
	 * 短信验证码过期时间
	 */
	private final int SMS_CODE_EXPIRE = 300;

	@Override
	public String getSms(String userName, Integer mark) {
		userRepository.findOne(1);
		// 验证手机号正确格式
		if (!RegexUtils.isMobile(userName))
			throw new BusinessException(
					SysSecurityMsgEnum.USER_NAME_FORMAT_IS_WRONG.getStatus(),
					SysSecurityMsgEnum.USER_NAME_FORMAT_IS_WRONG.getMsg());
		// 调用smsApi
		if (mark < 1 && mark > 3)
			throw new BusinessException(
					SysSecurityMsgEnum.USER_NAME_FLAG_IS_WRONG.getStatus(),
					SysSecurityMsgEnum.USER_NAME_FLAG_IS_WRONG.getMsg());
		// 验证码发送
		String code = SmsCodeUtils.generateCode();
		redisTemplateRepository.setEx(
				RedisCacheConsts.OVUOLA_SECURITY_NAME_SPACE, userName + mark,
				code, SMS_CODE_EXPIRE, TimeUnit.SECONDS);
		ThreadPoolExecutor.execute(() -> {
			// AbstractAliDayuSmsApi.send(userName, code);
			});
		// 返回数据
		return ResponseDataPackageUtils.success();
	}

	@TargetDataSource(DataSourceConsts.PRIMARY_DS)
	@Transactional(transactionManager = "transactionManagerDynamic", propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, readOnly = false, rollbackFor = Exception.class)
	@Override
	public String save(String appId, String nickName, Integer source,
			String headImage, String device) throws Exception {
		// 昵称校验
		if (nickName.length() > SysSecurityConst.NICK_NAME_LENGTH)
			throw new BusinessException(
					UserMsgEnum.NICK_NAME_LENGTH_IS_WORNG.getStatus(),
					UserMsgEnum.NICK_NAME_LENGTH_IS_WORNG.getMsg());
		if (StringUtils.isEmpty(appId))
			throw new BusinessException(
					CommonMsgEnum.SERVER_INTERNAL_ERROR.getStatus(),
					CommonMsgEnum.SERVER_INTERNAL_ERROR.getMsg());
		if (source < 1 || source > 4)
			throw new BusinessException(
					CommonMsgEnum.SERVER_INTERNAL_ERROR.getStatus(),
					CommonMsgEnum.SERVER_INTERNAL_ERROR.getMsg());
		UserDo userDo = userRepository.findByAppId(appId);
		if (userDo == null) {
			userDo = new UserDo(appId, nickName, headImage, source, device);
			userRepository.save(userDo);
		}
		// 个人生理基础信息
		PhysiologicalBasisDo physiologicalBasisDo = physiologicalBasisRepository
				.findByUserId(userDo.getId());
		UserContext userContext = setToken(userDo, appId);
		return ResponseDataPackageUtils.success(LoginUserAssembler
				.getAssembler()
				.toDto(userContext, userDo, physiologicalBasisDo));

	}

	/**
	 * 设置token
	 * 
	 * @param userDo
	 * @param user
	 * @return
	 */
	private UserContext setToken(UserDo userDo, String user) {
		UserContext userContext = generate(userDo.getId(), user);
		redisTemplateRepository.hset(
				RedisCacheConsts.OVUOLA_SECURITY_NAME_SPACE,
				userContext.getUser(), userContext.getToken(), userContext);
		redisTemplateRepository.expireAt(
				RedisCacheConsts.OVUOLA_SECURITY_NAME_SPACE,
				userContext.getUser(), new Date(userContext.getExpiration()));
		return userContext;
	}

	@TargetDataSource(DataSourceConsts.SECONDARY_DS)
	@Transactional(transactionManager = "transactionManagerDynamic", propagation = Propagation.REQUIRED, rollbackFor = Exception.class, readOnly = true)
	@Override
	public String login(String userName, String password) throws Exception {
		// 验证手机号正确格式
		if (!RegexUtils.isMobile(userName))
			throw new BusinessException(
					SysSecurityMsgEnum.USER_NAME_FORMAT_IS_WRONG.getStatus(),
					SysSecurityMsgEnum.USER_NAME_FORMAT_IS_WRONG.getMsg());
		// password 为空校验
		if (!RegexUtils.passwordSatify(password))
			throw new BusinessException(
					UserMsgEnum.PASSWORD_IS_WORNG.getStatus(),
					UserMsgEnum.PASSWORD_IS_WORNG.getMsg());
		UserDo userDo = userRepository.findByUserName(userName);
		if (!ObjectUtils.notEqual(userDo, null))
			throw new BusinessException(
					UserMsgEnum.USER_IS_NOT_EXIST.getStatus(),
					UserMsgEnum.USER_IS_NOT_EXIST.getMsg());
		// 校验密码
		if (StringUtils.isNotEmpty(userDo.getPassword())
				&& userDo.getPassword().equals(EncryptionUtils.md5(password))) {

			// 个人生理基础信息
			PhysiologicalBasisDo physiologicalBasisDo = physiologicalBasisRepository
					.findByUserId(userDo.getId());
			UserContext userContext = setToken(userDo, userName);
			return ResponseDataPackageUtils.success(LoginUserAssembler
					.getAssembler().toDto(userContext, userDo,
							physiologicalBasisDo));
		} else {
			throw new BusinessException(
					UserMsgEnum.PASSWORD_IS_WORNG.getStatus(),
					UserMsgEnum.PASSWORD_IS_WORNG.getMsg());
		}
	}

	/**
	 * 验证密码
	 * 
	 * @param userName
	 *            用户名
	 * 
	 * @param password
	 *            待验证密码
	 * @return
	 */
	@TargetDataSource(DataSourceConsts.SECONDARY_DS)
	@Transactional(transactionManager = "transactionManagerDynamic", propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, readOnly = true)
	@Override
	public String findPassword(String password, String userName) {

		// 密码格式校验
		if (!RegexUtils.passwordSatify(password)) {
			throw new BusinessException(
					UserMsgEnum.PASSWORD_FORMAT_IS_WORNG.getStatus(),
					UserMsgEnum.PASSWORD_FORMAT_IS_WORNG.getMsg());
		}

		// 查询
		UserDo result = userRepository.findByUserName(userName);
		if (!ObjectUtils.notEqual(result, null)) {
			throw new BusinessException(
					UserMsgEnum.USER_IS_NOT_EXIST.getStatus(),
					UserMsgEnum.USER_IS_NOT_EXIST.getMsg());
		}

		// 校验密码
		if (!result.getPassword().equals(EncryptionUtils.md5(password))) {
			throw new BusinessException(
					UserMsgEnum.PASSWORD_IS_WORNG.getStatus(),
					UserMsgEnum.PASSWORD_IS_WORNG.getMsg());
		}

		// 返回数据
		return ResponseDataPackageUtils.success();
	}

	/**
	 * 生成UserContext
	 * 
	 * @return
	 */
	private UserContext generate(Integer userId, String user) {
		UserContext userContext = new UserContext();
		userContext.setUserId(userId);
		userContext.setUser(user);
		userContext.setToken(TokenGenerateUtils.generateToken(userId, user));
		userContext.setExpiration(TokenGenerateUtils.generateEx());
		return userContext;

	}

	/**
	 * 修改绑定手机号
	 */
	@TargetDataSource(DataSourceConsts.PRIMARY_DS)
	@Transactional(transactionManager = "transactionManagerDynamic", propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, readOnly = false, rollbackFor = Exception.class)
	@Override
	public String updateUserName(String userName, String code, Integer mark,
			String oldUserName) {

		// 手机号格式验证
		if (!RegexUtils.isMobile(userName)) {
			throw new BusinessException(
					SysSecurityMsgEnum.USER_NAME_FORMAT_IS_WRONG.getStatus(),
					SysSecurityMsgEnum.USER_NAME_FORMAT_IS_WRONG.getMsg());
		}

		// 验证码校验
		String smsCode = (String) redisTemplateRepository.get(
				RedisCacheConsts.OVUOLA_SECURITY_NAME_SPACE, userName + mark);

		// 过期
		if (StringUtils.isBlank(smsCode)) {
			throw new BusinessException(
					SysSecurityMsgEnum.SMS_CODE_TIME_IS_WRONG.getStatus(),
					SysSecurityMsgEnum.SMS_CODE_TIME_IS_WRONG.getMsg());
		}
		// 错误
		if (!smsCode.equals(code)) {
			throw new BusinessException(
					SysSecurityMsgEnum.SMS_CODE_IS_WRONG.getStatus(),
					SysSecurityMsgEnum.SMS_CODE_IS_WRONG.getMsg());
		}

		// 用户不存在
		UserDo result = userRepository.findByUserName(oldUserName);
		if (!ObjectUtils.notEqual(result, null)) {
			throw new BusinessException(
					UserMsgEnum.USER_IS_NOT_EXIST.getStatus(),
					UserMsgEnum.USER_IS_NOT_EXIST.getMsg());
		}
		// 新手机号已经注册
		if (ObjectUtils.notEqual(userRepository.findByUserName(userName), null)) {
			throw new BusinessException(
					SysSecurityMsgEnum.USER_NAME_IS_REGISTER.getStatus(),
					SysSecurityMsgEnum.USER_NAME_IS_REGISTER.getMsg());
		}

		// 绑定
		result.setUpdateTime(new Date());
		result.setUserName(userName);
		userRepository.save(result);
		return ResponseDataPackageUtils.success();
	}

	/**
	 * 退出登录
	 */
	@TargetDataSource(DataSourceConsts.SECONDARY_DS)
	@Transactional(transactionManager = "transactionManagerDynamic", propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, readOnly = true, rollbackFor = Exception.class)
	@Override
	public String logoutLogin(String userName) {

		// 用户不存在
		if (!ObjectUtils
				.notEqual(userRepository.findByUserName(userName), null)) {
			throw new BusinessException(
					UserMsgEnum.USER_IS_NOT_EXIST.getStatus(),
					UserMsgEnum.USER_IS_NOT_EXIST.getMsg());
		}
		// 删除缓存-->退出操作
		redisTemplateRepository.del(
				RedisCacheConsts.OVUOLA_SECURITY_NAME_SPACE, userName);

		return ResponseDataPackageUtils.success();
	}

	/**
	 * 忘记密码
	 */
	@TargetDataSource(DataSourceConsts.PRIMARY_DS)
	@Transactional(transactionManager = "transactionManagerDynamic", propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, readOnly = false)
	@Override
	public String updatePassword(String userName, String password, String code,
			Integer mark, Integer userId) {
		// 用户格式
		if (!RegexUtils.isMobile(userName)) {
			throw new BusinessException(
					SysSecurityMsgEnum.USER_NAME_FORMAT_IS_WRONG.getStatus(),
					SysSecurityMsgEnum.USER_NAME_FORMAT_IS_WRONG.getMsg());
		}
		// 密码格式
		if (!RegexUtils.passwordSatify(password)) {
			throw new BusinessException(
					UserMsgEnum.PASSWORD_FORMAT_IS_WORNG.getStatus(),
					UserMsgEnum.PASSWORD_FORMAT_IS_WORNG.getMsg());
		}

		// 用户不存在
		UserDo result = userRepository.findByUserName(userName);
		if (!ObjectUtils.notEqual(result, null)) {
			throw new BusinessException(
					UserMsgEnum.USER_HARDWARD_IS_EXIST.getStatus(),
					UserMsgEnum.USER_HARDWARD_IS_EXIST.getMsg());
		}

		// 验证码校验
		String smsCode = (String) redisTemplateRepository.get(
				RedisCacheConsts.OVUOLA_SECURITY_NAME_SPACE, userName + mark);

		// 过期
		if (StringUtils.isBlank(smsCode)) {
			throw new BusinessException(
					SysSecurityMsgEnum.SMS_CODE_TIME_IS_WRONG.getStatus(),
					SysSecurityMsgEnum.SMS_CODE_TIME_IS_WRONG.getMsg());
		}
		// 错误
		if (!smsCode.equals(code)) {
			throw new BusinessException(
					SysSecurityMsgEnum.SMS_CODE_IS_WRONG.getStatus(),
					SysSecurityMsgEnum.SMS_CODE_IS_WRONG.getMsg());
		}

		// 更新时间
		result.setUpdateTime(new Date());
		result.setPassword(EncryptionUtils.md5(password));
		// 保存
		userRepository.save(result);
		return ResponseDataPackageUtils.success();
	}

}
